
-- Aythami Santana Monzon
-- Adrian Martin Parra

with Ada.Text_Io, Ada.Numerics.Discrete_Random;
use  Ada.Text_Io;


procedure ClientesGaraje is

	procedure mostrar_mensaje(mensaje: in String) is
	begin
		New_Line;
		Put_Line(mensaje);
	end;

	-- Define la situacion en la que puede estar una plaza de aparcamiento.
	type Estado is (libre, ocupado);

   	type Plaza is record
     		situacion: Estado := libre;
   	end record;


	-- Diferentes constantes para definir el comportamiento del modelo.
	NUM_PLAZAS  : Positive := 1;
   	NUM_PLANTAS : Positive := 2;
	NUM_CLIENTES: Positive := 4;
	DURACION    : Positive := 6;

	-- !Me cago en la putisima hostia de Cristo! Para hacer un delay con 
	-- random tengo que PERDER un numero aleatorio de horas buscando un 
	-- manual de Ada, leerlo y probar a ver que pasa.... Vamos, que al 
	-- final ni me acuerdo de para que queria el random.
   	subtype RangoAleatorio is Positive range 1..NUM_PLANTAS;
    package PaqueteAleatorio is new Ada.Numerics.Discrete_Random(RangoAleatorio);
   	use PaqueteAleatorio;
	semilla_aleatoria: PaqueteAleatorio.Generator;

   	subtype DuracionAleatoria is Positive range 1..DURACION;
    package PaqueteAleatorio2 is new Ada.Numerics.Discrete_Random(DuracionAleatoria);
   	use PaqueteAleatorio2;
	semilla_duracion: PaqueteAleatorio2.Generator;


	-- Planta de aparcamiento compuesta de un numero determinado de plazas.

	type Aparcamiento is array (Positive range <>) of Plaza;

   	protected type ConjuntoPlazas(maximo_plazas: Positive) is

      	procedure asignar_plaza(num_planta, num_plaza: in out Positive);
		procedure devolver_plaza(num_planta, num_plaza: in Positive);
		function hay_plazas_libres return Boolean;

   	private

      	plazas_libres      : Natural := maximo_plazas;
    	plazas_aparcamiento: Aparcamiento(1..maximo_plazas);

   	end ConjuntoPlazas;
	

   	protected body ConjuntoPlazas is

		procedure asignar_plaza(num_planta, num_plaza: in out Positive) is
			i: Positive := 1;
      	begin

        	-- Busqueda y asignacion de una plaza libre
			while plazas_aparcamiento(i).situacion = ocupado loop
				i := i + 1;
			end loop;

			-- Cuando salimos, es que hemos encontrado una plaza libre. La
			-- ocupamos y actualizamos las variables pertinentes.
			plazas_aparcamiento(i).situacion := ocupado;
            plazas_libres := plazas_libres - 1;
			num_plaza     := i;
		
			mostrar_mensaje("Asignada la plaza " & i'Img & " en la planta " 
							& num_planta'Img);
	
      	end asignar_plaza;

		procedure devolver_plaza(num_planta, num_plaza: in Positive) is
		begin

			plazas_aparcamiento(num_plaza).situacion := libre;
			plazas_libres := plazas_libres + 1;

		end devolver_plaza;

		function hay_plazas_libres return Boolean is
		begin

			return plazas_libres > 0;

		end hay_plazas_libres;

	end ConjuntoPlazas;


	-- Se define un parking como una estructura formada por diversas plantas,
	-- donde cada una de ellas tiene un numero dado de plazas de aparcamiento.
	-- Este tipo es el encargado de gestionar las entradas y salidas. El tipo 
	-- PlantaAparcamientosolamente es responsable de buscar y liberar una 
	-- plaza en el interior de una planta.

	type PlantaAparcamiento is array(Positive range <>) of ConjuntoPlazas(NUM_PLAZAS);

   	protected type Parking(maximo_plantas: Positive) is

		entry entrar_parking(num_planta, num_plaza: in out Positive);	
		procedure salir_parking(num_planta, num_plaza: in Positive);
		entry esperar(num_planta, num_plaza: in out Positive);

   	private

		procedure buscar_aparcamiento(num_planta, num_plaza: in out Positive;
									  tengo_plaza: in out Boolean);

		plantas  : PlantaAparcamiento(1..maximo_plantas);
		liberados: Boolean := False;
		esperando: Natural := 0;
	
   	end Parking;

	protected body Parking is

		entry entrar_parking(num_planta, num_plaza: in out Positive) 
			when True is 

			tengo_plaza: Boolean := True;
    	begin

			num_planta := Random(semilla_aleatoria);

			mostrar_mensaje("Llega un cliente a la planta " & num_planta'Img);

			buscar_aparcamiento(num_planta, num_plaza, tengo_plaza);
			if not tengo_plaza then 
				requeue esperar; 
			end if ;

  		end entrar_parking;


		procedure salir_parking (num_planta, num_plaza: in Positive) is
		begin
			
			mostrar_mensaje("Se saca un vehiculo de la planta " & num_planta'Img
							& ", plaza " & num_plaza'Img);

			plantas(num_planta).devolver_plaza(num_planta, num_plaza);

			if esperar'Count > 0 then
				esperando := esperar'Count;
				liberados := True;
			end if;
				
		end salir_parking;


		entry esperar(num_planta, num_plaza: in out Positive) 
			when liberados is

			tengo_plaza: Boolean := True;
		begin

			esperando := esperando - 1;

			if esperando = 0 then liberados := false;
			end if;
		
			buscar_aparcamiento(num_planta, num_plaza, tengo_plaza);
			if not tengo_plaza then 
				requeue esperar; 
			end if;

		end esperar;


		procedure buscar_aparcamiento(num_planta, num_plaza: in out Positive;
									  tengo_plaza: in out Boolean) is
		begin
			
			if plantas(num_planta).hay_plazas_libres then
				-- Buscamos una plaza en esa planta.
        		plantas(num_planta).asignar_plaza(num_planta, num_plaza);
			else 
				-- Que pruebe suerte en otra planta...
				num_planta := Random(semilla_aleatoria);
				mostrar_mensaje("Reencolando a la planta " & num_planta'Img);
				tengo_plaza := False;
			end if;

		end buscar_aparcamiento;
		
	end Parking;


	recinto_aparcamiento: Parking(NUM_PLANTAS);

	-- Usuario del parking.	
   	task type Cliente;

   	task body Cliente is
		num_planta, num_plaza: Positive := 1;
   	begin
		-- Estaciono mi vehiculo en el aparcamiento (estaria bien que los
		-- clientes no llegaran todos a la vez, sino con un pequeño
		-- retraso).
      	recinto_aparcamiento.entrar_parking(num_planta, num_plaza);

		-- Me voy a dar un paseo, though...(estaria bien poner un random).
		delay Duration(Random(semilla_duracion));

		-- Vuelvo y recojo mi coche supertuneado. Espero que no me lo
		-- hallan rallado...
      	recinto_aparcamiento.salir_parking(num_planta, num_plaza);

   	end Cliente;

   	clientes: array(1..NUM_CLIENTES) of Cliente; 

begin

	Reset(semilla_aleatoria);  -- Esta mariconada aqui "potencia la aleatoriedad"
	Reset(semilla_duracion);  

end ClientesGaraje;
